﻿/*
 * 2011-06-07，陈洋洋
 * 改进版HttpServer，学习参考二级工程实践的那个linux下的比较不错的httpserver
 * 完善功能
 * 完善基本功能后，再添加sqlite
 * 2011-07-30，陈洋洋
 * 准备改进。
 * 增加一个socket的长连接函数，用来接受控制机器人的命令，
 * 并且将机器人返回的信息返回到客户端或者是浏览器的网页上
 * 
 * 预留两个接口用于机器人控制
 * 1、http长连接，用于自定义的http客户端控制机器人
 * 2、http短连接，用于网页控制机器人
 * 
 * 正在改进：
 * 1、增加websocket连接功能，支持双工通信，支持http推送
 * 
 * 增加：Handler接受处理消息，接受处理MainActivity发送的消息
 * 增加接口，向MainActivity发送消息
 */
package com.robotcontroller.buaa;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;

import org.apache.http.util.ByteArrayBuffer;

import android.os.Handler;
import android.util.Base64;
import android.util.Log;

public class MyHttpServer
{
	private Handler mHandler = null;
	
	private byte[] httpReqBuf = new byte[1024*5];   //客户端发送的HTTPREQUEST信息，5KB足够
	private int httpReqLen = 0;                     //客户端发送的HTTPREQUEST信息的长度
	private int websocketbuflen = 1021*10;          //websocket服务器端接受缓存
	
	//HTTPRESRESPONSE的一部分
    private  String partheader = "Connection: close\r\n"
        +"Server: RobotController1.0\r\n" 
        +"Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n"
        +"Pragma: no-cache\r\n"
        +"Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n";
    
    private  String boundary = "---------------------------7da3d81520810";
    private String strlocaladd = null; 
    int port;
    
	private static webSocketServer webSSThread = null;
	private static boolean isWebSocketConnected = false;
	public static boolean IsWebSocketConnected()
	{
		return isWebSocketConnected;
	}
	
	public MyHttpServer(int nport , Handler handler)
	{
		mHandler = handler;
		port = nport;
		new ListenThread(port).start();
	}
	
	
	private class ReqType
	{
	    String httpmethod = new String();
		int reqtype; //请求类型，请求文件，请求视频流等
		byte[] argv = new byte[50];  //请求 的参数，例如请求的是控制机器人，控制机器人命令，参数 
	}
	
	
	/*
	 * 监听线程
	 * 监听8080端口，是否有tcp socket连接请求
	 * 如果有，则接受客户端连接请求，创建新的线程ProcessReqThread去处理请求内容
	 * 循环监听 
	 */
	public class ListenThread extends Thread
	{
		int port;
		ServerSocket serversocket;
		Socket commsocket;

		public ListenThread(int nport)
		{
			port = nport;
		}
		
		public void run()
		{
			try
			{
				 serversocket = new ServerSocket(port);
				 //serversocket.setReuseAddress(true); //设置地址重用，为何异常？			 
				 strlocaladd = getLocalIpAddress()+":"+ port;
				 edithtmlfile("index.html");  //替换其中的192.168.0.8:8080为当前使用地址			 
			} catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
				Log.i("cyy" , "bind port exception");
				return ;
			}
			
			
			//循环监听
			while(true)
			{
				try 
				{
					commsocket = serversocket.accept();					
					//Log.i("cyy" , "local address is:"+strlocaladd);
					//Log.i("cyy" ,"new connection form client:"+commsocket.getRemoteSocketAddress());
				} catch (IOException e) 
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
					Log.i("cyy" , "accept exception");
					return;
				}
				
				new ProcessReqThread(commsocket).start();			
			}	
		}
	}
	
	/*
	 * 处理线程
	 * 接收连接成功的客户端发送的HTTPREQUEST信息，
	 * 解析HTTPREQUEST的内容
	 * 根据HTTPQEQUEST和本地的配置来处理客户端的请求，
	 *     或者发送文件，发送视频流，发送错误，发送权限验证等
	 */
	public class ProcessReqThread extends Thread
	{
		Socket csocket;//连接成功后返回的socket，与客户端通信
		DataInputStream dis;
		DataOutputStream dos;
		public ProcessReqThread(Socket nsockt)
		{
			csocket = nsockt;
		}
		
		public void run()
		{
			try 
			{
				dis = new DataInputStream(csocket.getInputStream());
				dos = new DataOutputStream(csocket.getOutputStream());
			} catch (IOException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
				Log.i("cyy" , "");
				return ;
			}
		   
			//注意参数的传递
			/*
			 * 1、接受httpReq
			 * 2、解析httpReq，如果非法，则senderror!
			 * 3、发送httpReponse
			 */
			
			 try
			 {
				 //读取第一行信息，类似GET HTTP/1.1
				String firstLine = dis.readLine();
					//Log.i("cyy" , "firline httpReq:" + firstLine);
					//读取剩余信息，主要提取Authorization: Basic字段后面的值，然后提取解码和验证				
				httpReqLen = dis.read(httpReqBuf);
				String restLines = new String();
				
				try
				{
					restLines = new String(httpReqBuf, 0, httpReqLen);
				}catch (Exception e) 
				{
					e.printStackTrace();
				}

				    //Log.i("cyy" , "rest httpReq:" + restLines);
				//Log.i("cyy", "HTTPREQ:\r\n"+firstLine +"\r\n"+ restLines);
				
				/*
				 * 判断是否是WebSocket的握手请求
				 * 如果是，则开启新的线程去处理，发送握手信息
				 * 握手成功后，使用该socket去全双工通信，用于控制机器人行为
				 */
				if(-1 != restLines.indexOf("Upgrade: WebSocket"))
				{					
                    //开启线程
					webSSThread = new webSocketServer(csocket, dis, dos, restLines);
					webSSThread.start();
					return;
				}
				
				
				int beginpos = restLines.indexOf("Authorization: Basic");  //注意空格
				if(-1 == beginpos)
				{
					sendError(dos, csocket, 401 , "Authorization need");  //Authorization need
					dis.close();
					return ;
				}
				else
				{
					//提取字段后面的值，解析
					beginpos = beginpos + "Authorization: Basic".getBytes().length + 1 ; //+1为空格
					int endstr = restLines.indexOf("\r\n" , beginpos);
					String idkeydecoded = restLines.substring(beginpos, endstr);
					//Log.i("cyy" , "ID code is "+idkeydecoded);
					byte[] idkeyencoded = Base64.decode(idkeydecoded , 0);
					String idkey = new String(idkeyencoded);
					
					int beginstr = idkey.indexOf(':');
					String id = idkey.substring(0, beginstr); 
					String key = idkey.substring(beginstr+1);  //不要忘记了这个1
                    
					//Log.i("cyy" , "new .id is:"+id + ".key is:"+key);					
					//Log.i("cyy" , "ID right? "+ id.equals("cyy"));
					//Log.i("cyy" , "Key right? " + key.equals("1234"));
					
					if( !( id.equals("cyy")&&key.equals("1234")) )
					{
						sendError(dos, csocket, 401, "ID or key error,please check");
						dis.close();
						return ;
					}

				}
				 //解析成功
				//Log.i("cyy" , "httpRequset, Authorization OK! ");
				
				if( -1 == firstLine.indexOf("GET") )
				{
					sendError( dos, csocket , 501 , "only surpport GET method"); //"only surpport GET method"
					dis.close();
					return ;
				}
				else
				{
					
					//判断请求内容的类型，关键是提取连个空格直接的内容
					/*
					 *   规定，GET /jpeg-stream HTTP/1.0  发送JPEG图像帧流
					 *         GET /command:godir1 HTTP/1.0  发送控制机器人，命令机器人沿着DIR1方向行走
					 *         GET /http://192.168.0.8:8080 HTTP/1.0  返回index.html
					 *         GET /1.text HTTP/1.0 返回1.text
					 */
					int beginstr = firstLine.indexOf(' ');    //第一个空格的位置

					int endstr = firstLine.indexOf(' ' , beginstr+1);   //第二个空格的位置
					//获取GET后面的请求内容，还有一个空格
					//String reqobject = firstLine.substring(beginstr  , endstr);  //包括beginstr，不包括endstr
					String reqobject = firstLine.substring(beginstr+1  , endstr);
					//Log.i("cyy" , "client request is "+reqobject);
					 
					if(reqobject.equals("/"))
					{
						//请求的是默认的index.html文件
						//Log.i("cyy" , "will send index.html");
						String filename = reqobject;
						sendFile(dos, csocket , filename);
						dis.close();
						return ;
					}
					else if(reqobject.equals("/jpeg-stream"))
					{
						sendStreamHttpMXMR(dos, csocket , null);
						dis.close();
						return ;
					}
					else if("/command:" == reqobject.substring(0))
					{
						processRbtCmd(reqobject);  
						dis.close();
						return ;
					}
					else
					{
						String filename = reqobject;
						sendFile(dos, csocket , filename);
						//sendError(dos , csocket , 501 , "Request type not surpport!"); //"Request type not surpport!"						
						dis.close();
						return ;
					}
										
							
					
				}
				
				
			} catch (IOException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
		}
	}
	
	//some bugs
	//服务器向客户端推送信息，目前先使用websocket实现
	public static void pushToClient(byte[] buffer , int offset, int count)
	{
		if(null!=webSSThread && isWebSocketConnected)
		{
			webSSThread.writeFrame(buffer, offset, count);
			Log.i("cyy", "pushToClient .....");
		}
	}
	
	/*
	 * 解析客户端发送的HTTPREQUEST信息
	 * 同时也包括网页发送的控制机器人的命令
	 * 基本功能：
	 *   1、接受客户端的HTTPREQUEST到httpReqBuf，长度为httpReqLen
	 *   2、查找Authorization: Basic ，进行解析（第一次），权限是否符合，否则发送错误信息
	 *      （后期可以改进http Oauth方式验证，更加安全）
	 *   2、解析客户端的请求类型，只响应GET请求，暂时不响应post等其他请求（查找整个httpReqBuf）
	 *   3、解析客户端的请求，请求html文件，请求视频流，请求控制机器人
	 *   
	 *   规定，GET jpeg-stream  发送JPEG图像帧流
	 *         GET command:godir1  发送控制机器人，命令机器人沿着DIR1方向行走
	 *         GET http://192.168.0.8:8080  返回index.html
	 *         GET 1.text 返回1.text
	 */
	
	private ReqType parseReq(byte[] buffer , int len)
	{
		ReqType reqtype = new ReqType();
		
		
		String temp = new String(buffer);		
		
		return reqtype;
	}
	
	
	
	
	/*
	 * 发送文件
	 * 1、查找文件，查找失败，则发送sendError()，文件未找到信息
	 * 2、查找成功后，读取文件，组织成HTTP信息发送
	 * 目前是，小文件读取，如果文件太大，可能非常占用内存
	 */
	private void sendFile(DataOutputStream dos , Socket socket , String filename)
	{
		if(filename.equals("/"))
		{
			filename = "\\index.html";
		}
	
		File file = new File("\\mnt\\sdcard\\RobotController_WWW"+filename);
		FileInputStream fis ;
		try
		{
			 fis = new FileInputStream(file);
		} catch (FileNotFoundException e)
		{
			sendError(dos, socket, 404 , "file: "+filename+" not found.");
			e.printStackTrace();
			return ;
		}
		
		//改进措施，一次只读取一部分，再传送。。。目前是图省事
		int filelen = (int)file.length();
		byte[] filebuf = new byte[filelen];
		
	    DataInputStream dis = new DataInputStream(fis);
	    try
	    {
			dis.readFully(filebuf);
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			sendError(dos, socket, 500 , "read file error! please acces later!");
			e.printStackTrace();
			return ;
		}
		
		try 
		{
			dos.write(filebuf);
			dos.flush();
			dos.close();
			socket.close();
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		
	}
	
	/*
	 * 发送视频流，以HTTP multipart/x-mixed-replace协议发送
	 */
	private void sendStreamHttpMXMR(DataOutputStream dos , Socket socket , String argv)
	{
		//argv为请求图片帧的质量的要求。。。后期再添加
		Socket commusocket = socket;
		DataOutputStream outstream = dos;
		int mjpeglength = 0;
		ByteArrayOutputStream byteoutputstream = new ByteArrayOutputStream();
		                                      
	
		   //1、HTTP信息头 						
		   String jheader = "HTTP/1.0 200 OK\r\n"
					       +partheader	                  
					       +"Content-Type: multipart/x-mixed-replace;boundary="+boundary+"\r\n"
					       +"\r\n"
					       +"--"+boundary+"\r\n";
		   
		  try 
		  {
			outstream.write(jheader.getBytes(), 0 , jheader.getBytes().length);
			outstream.flush();
		  } catch (IOException e1)
			{
				// TODO Auto-generated catch block
			    try 
			    {
					socket.close();
				} catch (IOException e) 
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				e1.printStackTrace();
				return ;
			}
			  
		  
		  
		    long begintime = System.currentTimeMillis();
		    long endtime;
		    long frames = 0;
			//2、再发送jpeg流
		    while(MainActivity.NOSTOPED)
		     {	 
			    	
			    	//貌似不起作用,该句不启作用，如何判断socket远端关闭呢？
			    	//需要另外开启线程维护心跳，也就是活跃状态信息？
			    	//学习下，长连接，短连接的问题！！！
					if(commusocket.isClosed())  
					{
						Log.i("cyy" , "port 8080,socket closed,exit...");
						break;		
					}
	
	
					try 
					{
						mjpeglength = MainActivity.decodetojpeg(byteoutputstream);
					} catch (Exception e) 
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				
					
					if(mjpeglength == 0)
					{
						Log.i("cyy" , "error ,jpenglength =0!!!");
						
					}
					
                    //开销比较大，最好改进为byte[]数组
					String httpheader 
					= "Content-Type: image/jpeg\r\n"
	                + "Content_Length: " + mjpeglength +"\r\n"
	                + "\r\n";
					
					//不要再循环里面跑出异常，改进，挪到外面
					try 
					{
						//发送帧头
						outstream.write(httpheader.getBytes() , 
								        0 , httpheader.getBytes().length);
						outstream.flush();
						
						//发送JPEG
						outstream.write(byteoutputstream.toByteArray() ,
								        0 , mjpeglength);
						outstream.flush();
					
						//发送分隔符
						String strbunde = "\r\n--" +boundary +"\r\n";
						outstream.write(strbunde.getBytes() ,
								        0 , strbunde.length() );
						outstream.flush();
						
					} catch (IOException e) 
					{
						// TODO Auto-generated catch block
						try
						{
							socket.close();
							outstream.close();
						} catch (IOException e1) 
						{
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}
						Log.i("cyy" , "send jpeg stream, done, closing....");
						e.printStackTrace();
						break;
					}
					
	                frames++;
					if(0 == frames%50)
					{
						endtime = System.currentTimeMillis();
						//Log.i("cyy" , "frame rate is "+ frames/((endtime-begintime)/1000)+" pf" );
					}
				    //timeend = System.currentTimeMillis();

		     }//endwhile
				
	}
	
	/*
	 * 发送视频流，以HTML5 websocket协议发送
	 */
	private void sendStreamHtml5Websocket(DataOutputStream dos , Socket socket , String argv)
	{
		
	}
	
	
	/*
	 * 发送错误信息
	 * 其中包含权限错误信息
	 * 1、权限不足
	 * 2、请求文件不存在
	 * 3、请求命令不支持
	 */
	private void sendError(DataOutputStream dos , Socket socket, int errortype , String errorinfo)
	{
          String httpReponse = new String();
		  switch(errortype)
		  {
			  case 401:
			  {
				  httpReponse = "HTTP/1.0 401 Unauthorized\r\n"
                     +"Content-type: text/plain\r\n"
                     + partheader
                     + "WWW-Authenticate: Basic realm=\"RobotController1.0_HTTP\"\r\n"
                     + "\r\n" 
                     + "401: Not Authenticated!\r\n" 
                     + errorinfo;
				  break;
			  }
			  case 404:
			  {
				  httpReponse =  "HTTP/1.0 404 Not Found\r\n"
                     + "Content-type: text/plain\r\n"
                     + partheader
                     + "\r\n"
                     + "404: Not Found!\r\n"
	                 + errorinfo;
				  break;
			  }
			  case 500:
			  {
				  httpReponse = "HTTP/1.0 500 Internal Server Error\r\n"
                    + "Content-type: text/plain\r\n"
                    + partheader
                    + "\r\n"
                    + "500: Internal Server Error!\r\n"
                    + errorinfo;
				  break;
			  }
			  case 400:
			  {
				  httpReponse = "HTTP/1.0 400 Bad Request\r\n"
                    + "Content-type: text/plain\r\n"
                    + partheader
                    + "\r\n"
                    + "400: Not Found!\r\n"
                    + errorinfo;
				  break;
			  }
			default:
			  {
				  httpReponse = "HTTP/1.0 501 Not Implemented\r\n"
                    + "Content-type: text/plain\r\n"
                    + partheader
                    + "\r\n"
                    + "501: Not Implemented!\r\n"
                    + errorinfo;
			  }
		  }
	       
			try
			{
				dos.write( httpReponse.getBytes() );
				dos.flush();
				socket.close();
			} catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
	}
	
	/*功能：
	 * 1、处理客户端的发送机器人协议命令
	 * 2、蓝牙发送给机器人
	 * 3、接受机器人发送的蓝牙信息，推送到客户端，
	 * 使用到http长连接
	 * 
	 */
	private void processRbtCmd(String cmd)
	{
		
	}
	
	private class webSocketServer extends Thread
	{
		private Socket msocket;
		private DataInputStream mdis;
		private DataOutputStream mdos;
		private String strhandshakereq;  //client发送的请求握手的信息

		
		public webSocketServer(Socket socket, DataInputStream dis, DataOutputStream dos ,String str)
		{
			msocket = socket;
			mdis = dis;
			mdos = dos;
			strhandshakereq = str;
			isWebSocketConnected = false;
		}
		
		/*
		 * 1、握手
		 * 2、循环读取
		 */
		@Override
		public void run()
		{
			//

			//提取“Sec-WebSocket-Key1:”后面的值
			//提取“Host:”后面的值，例如“Host: 192.168.0.6:8080”
			//提取“Origin:”后面的值，例如Origin: http://192.168.0.6:8080
			String key1 = getfield(strhandshakereq, "Sec-WebSocket-Key1:");
			String key2 = getfield(strhandshakereq, "Sec-WebSocket-Key2:");					
			String host = getfield(strhandshakereq, "Host:");  									
			String origin = getfield(strhandshakereq, "Origin:");
						
			/* 从最后一个“\r\n”和倒数第二个“\r\n”之间提取出来key3
			 * 至今难以解决的bug。
			 * 实际接收到8个字节2E:  6C:   BA:  B1:  DF:  ED:  F4: 07
			 * 转换字符串后再getbytes变成了46 108  -17  -65  -67  -17  -65 -67 -17 -65 -67 -17 -65 -67 -17 -65 -67 7
			 * 多了好几位？？？  
			   beginpos = restLines.lastIndexOf("\r\n");				
			   String key3 = restLines.substring(beginpos+2);
			   Log.i("cyy" , "key3 len:"+key3.getBytes().length);
			   for(int i=0 , len=key3.getBytes().length ; i<len; i++)
			   {
				  Log.i("cyy" , " "+ByteToUnint(key3.getBytes()[i]));
			   }
			 */			 										
			
			//获取Key3，接收到的最后的8个字节
			byte[] key3part = new byte[8];
			System.arraycopy(httpReqBuf, httpReqLen-8, key3part, 0, 8);
			
			//算法一，按照无符号int处理，使用long类型的第四位字节					
			 long key1number = Long.parseLong(ExtractKeyNumber(key1));
			 long key2number = Long.parseLong(ExtractKeyNumber(key2));
			 int  key1spaces = extractSpaces(key1);
			 int  key2spaces = extractSpaces(key2);					 
			 long key1part = key1number / key1spaces;
			 long key2part = key2number / key2spaces;
			 							
			
		   //算法二，按照int类型处理，经常发生上溢导致错误
			 /*
			 int key1number = extractKeyNumber(key1);
			 int key2number = extractKeyNumber(key2);
			 int key1spaces = extractSpaces(key1);
			 int key2spaces = extractSpaces(key2);
			 if (key1spaces == 0 || key2spaces == 0) 
			 {
			      throw new IOException("Incorrect WebSocket handshake");
			 }
			 
			 int key1part = key1number / key1spaces;
			 int key2part = key2number / key2spaces;
			*/
			
			 
			if (key1part * key1spaces != key1number
			        || key2part * key2spaces != key2number) 
			 {
				  Log.i("cyy" ,"key1:"+key1+"\r\n" 
						  +"key1part:"+key1part+"\r\n" 
						  +"key1part float:"+((float)key1number/key1spaces)+"\r\n"
						  +"key1spaces:"+key1spaces+"\r\n" 
						  +"key1number:"+key1number+"\r\n" 
						  );
				  Log.i("cyy" ,"key2:"+key2 +"\r\n" 
						  +"key2part float:"+((float)key2number/key2spaces)+"\r\n"
						  +"key2part:"+key2part+"\r\n" 
						  +"key2spaces:"+key2spaces+"\r\n" 
						  +"key2number:"+key2number+"\r\n" 
						  );
			       try  {
					throw new IOException("Incorrect WebSocket handshake");
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
			 }				
			
			
			MessageDigest md5;
			try 
			{
			  md5 = MessageDigest.getInstance("MD5");
			} catch (NoSuchAlgorithmException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
				return;
			} 
			
			
			//算法二
		     /*
			 byte[] intBytes = new byte[4];
			 intToBytes(key1part, intBytes);
			 md5.update(intBytes);
			 intToBytes(key2part, intBytes);
			 md5.update(intBytes);
			 md5.update(key3part);
		    */
			
			//算法一，long类型取第四位字节				 
			byte[] longbytes = new byte[4];
			lownlongToBytes(key1part, longbytes);
			md5.update(longbytes);
			lownlongToBytes(key2part, longbytes);
			md5.update(longbytes);
			md5.update(key3part);
								
            String sendStr 
                = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
	            +"Upgrade: WebSocket\r\n"
	            +"Connection: Upgrade\r\n"
	            +"Sec-WebSocket-Origin: "+ origin +"\r\n"
	            +"Sec-WebSocket-Location: "+"ws://"+ host + "/" +"\r\n"
	            +"\r\n";
			
            //发送握手信息
            try
            {
				mdos.write(sendStr.getBytes() ,  0 , sendStr.getBytes().length);
	            mdos.write( md5.digest() );
	            mdos.flush();
			} catch (IOException e)
			{
				clean();
				e.printStackTrace();
				return ;
			}

            
            Log.i("cyy" , "WebSocket send:\r\n"+sendStr);
            
            //休眠5秒
            try 
            {
				sleep(5000);
			} catch (InterruptedException e1) 
			{
				e1.printStackTrace();
			}
            
            byte[] readbuffer = new byte[websocketbuflen]; //10KB 
           // byte[] readbuffer = new byte[32]; //蓝牙协议命令最多32个 
            
            int readlen = 0;
            while(true)
            {
            	isWebSocketConnected = true;
            	
            	try 
            	{
            		//如果信息太长....需要组帧
					readlen = mdis.read(readbuffer);

					if(-1 == readlen) //OK，当对方关闭时候，再读取，该值就为-1
					{
						Log.i("cyy" , "websocket , read number:" + readlen + ",will exit:");
						break;  
					}
					
					Log.i("cyy" , "websocket, read:");
					for(int i=0 ; i<readlen ; i++)
					{
						Log.i("cyy" , " readbuffer["+ i +"]:"+readbuffer[i]+",");
					}
					
					//验证帧的正确与否					
					if(0x00 != readbuffer[0] || 0xFF!= ByteToUnint(readbuffer[readlen-1]) )
					{
						Log.i("cyy" , "websocket, readFrame error!");
					}
					
					//只提取readbuffer[1]到readbuffer[readlen-2]
					//发送给MainActivity，让MainActivity去解析										
					mHandler.obtainMessage(MainActivity.MHSMSG_TOROBOT , 1 
							               , readlen-2, readbuffer).sendToTarget();

					//a test,for debug
					//byte[] test = {0x01 , 0x12 , 0x12 , 0x15};
					//writeFrame(test, 0, test.length);
					
				} catch (IOException e)
				{
					clean();
					e.printStackTrace();
					Log.i("cyy" , "websocket ,IOException ,will exit:");
					break;
				}
            }
            
            isWebSocketConnected = false;

		
		}//endwhile
		
		/*0x00 ....... 0xFF，websocket定义的帧格式
		 * 
		 */
		void writeFrame(byte[] buffer , int offset , int count)
		{
            try 
            {
				mdos.writeByte(0x00);
	            mdos.write(buffer , offset , count);
	            mdos.writeByte(0xFF);
	            Log.i("cyy" , "in writeFrame()...writing...");
			} catch (IOException e)
			{
				// TODO Auto-generated catch block
				clean();
				e.printStackTrace();
			}

		}
		
		/*0x00 ....... 0xFF，websocket定义的帧格式
		 * 或许会有些bug,当消息比较长的时候
		 */
		void readFrame(byte[] buffer , int offset , int count)
		{
			try
			{
				mdis.readFully(buffer, 1, count-2);
			} catch (IOException e)
			{
				clean();
				e.printStackTrace();
			}
			
		}
		
		void clean()
		{
			try
			{
				isWebSocketConnected = false;
				mdis.close();
				mdos.close();
				msocket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		

	}
	
	/*找到websocket2.html文件，修改其中的webserveradd = 'ws://192.168.0.9:8080/'
	 *修改为webserveradd = 'ws://+ strlocaladd + /'
	 */
	private void edithtmlfile(String strhtmlname)
	{
		
		String htmlname = strhtmlname;
		File file = new File("\\sdcard\\RobotController_WWW\\" + htmlname);
		FileInputStream fis ;
		FileOutputStream fous;
		try
		{
			 fis = new FileInputStream(file);
			 //fous = new FileOutputStream(file);
		} catch (FileNotFoundException e)
		{
			//sendError(dos, csocket, 404 , "file: " + htmlname + " not found.");
			e.printStackTrace();
			return ;
		}
		
		int filelen = (int)file.length();
		byte[] filebuf = new byte[filelen];
		//Log.i("cyy" , "file length:"+filelen);
		
	    DataInputStream dis = new DataInputStream(fis);
	    try
	    {
			dis.readFully(filebuf);
			dis.close();
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			//sendError(dos, csocket, 500 , "read file error! please acces later!");
			e.printStackTrace();
			return ;
		}
	    //查找，替换，写回文件
		String strhtml = new String(filebuf , 0 , filebuf.length );
		
		if(-1 != strhtml.indexOf(strlocaladd))
		{
			return ;
		}
		
		/*
		 * 查找var webserveradd所在的这一行。
		 * 将这行整理替换为var webserveradd = var webserveradd = 'ws:// + strlocaladd + /' ;
		 */		
		int beginpos = strhtml.indexOf("var webserveradd");
		int endpos = strhtml.indexOf("\r\n", beginpos);
		if(-1==beginpos || -1==endpos)
		{
			Log.i("cyy" , "edit file index.html error! Please check the file!");
			try {
				fis.close();
				dis.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}			
			return ;
		}
		String oldadd = strhtml.substring(beginpos, endpos);
		String newadd = "var webserveradd = "+"'ws://" + strlocaladd + "/';" + "\r\n";
		String newhtml = strhtml.replaceFirst(oldadd , newadd );			

		//写回文件
	    try
	    {
	    	fous = new FileOutputStream(file); //一旦构造该FileOutputStream，文件将会被置0，没有内容。。。！！！！
			fous.write(newhtml.getBytes() , 0 , newhtml.getBytes().length);
			fous.flush();
			fous.close();
		} catch (IOException e) {
			Log.i("cyy" , "write file:"+ htmlname + " error!");
			e.printStackTrace();
		}
	    
	}
	
	
	/*
	 * 获取本机正在使用的IP，不是Localhost
	 */
    public static String getLocalIpAddress()
    {     
        try {     
            for (Enumeration<NetworkInterface> en = NetworkInterface     
                    .getNetworkInterfaces(); en.hasMoreElements();) {     
                NetworkInterface intf = en.nextElement();     
                for (Enumeration<InetAddress> enumIpAddr = intf     
                        .getInetAddresses(); enumIpAddr.hasMoreElements();) {     
                    InetAddress inetAddress = enumIpAddr.nextElement();     
                    if (!inetAddress.isLoopbackAddress()) {     
                        return inetAddress.getHostAddress().toString();     
                    }     
                }     
            }     
        } catch (SocketException ex) {     
            Log.e("WifiPreference IpAddress", ex.toString());     
        }     
        return null;     
    }
	
	 public static String ExtractKeyNumber(String str)
	 {
		 if(null == str)
		 {
			 return null;     //返回一个空，注意。不是空格字符
		 }
		 
		 byte[] c = str.getBytes();
		 byte[] tmp = new byte[c.length];
		 int lenth=0;
		 
		// String strNumber = new String();
		 
		 for(int i=0 ,len=c.length; i<len ; i++ )  //优化，不必每次都计算c.length
		 {
			 if( c[i]>='0' && c[i]<='9')  //c[i]为数字字符
			 {
				 //strNumber  = strNumber + c[i];
				 //System.out.print(" "+c[i]);
				 tmp[lenth] = c[i];
				 lenth ++;
			 }
		 }
		 
		 return new String(tmp, 0, lenth);
		 
	 }
	 
	 
	 /*
	  * 将str中的非空白字符剔除，返回str中空字符组成的字符串
	  * 例如"awe 234 2"为"  "
	  */
	 public static String ExtractSpaces(String str) 
	 {
		 if(null == str)
		 {
			 return null;  //返回一个空格字符
		 }
		 
		 byte[] c = str.getBytes();
		 //String strNonEmpty = new String();
		 byte[] tmp = new byte[c.length];
		 int lenth=0;
		 
		 for(int i=0,len=c.length ; i<len ; i++)
		 {
			 if(' ' == c[i])
			 {
				 //strNonEmpty = strNonEmpty + c[i];
				 //System.out.print(" "+c[i]);
				 tmp[lenth] = c[i];
				 lenth ++;
			 }
		 }
		 

		 return new String(tmp, 0, lenth);
	 }
	 
	 public static String Bigendian(String str)
	 {
		 byte[] cc = str.getBytes();
		 byte[] c = new byte[str.getBytes().length];
		 
		 for(int i=0 ,len=cc.length ; i<len ; i++)
		 {
			 c[i] = cc[len-1-i];
		 }
		 
		 return new String(c, 0 , c.length);
	 }
	 
		  /**
		   * @return draft number, or -1 if none present
		   */
		  protected int getDraftNumber() {
		    return 2;
		  }

		  /**
		   * @param key
		   * @return the value, ignoring non-digits, of the key
		   */
		  private int extractKeyNumber(String key)
		  {
			    int val = 0;
			    for (int i = 0; i < key.length(); ++i) 
			    {
			      char ch = key.charAt(i);
			      if (ch >= '0' && ch <= '9') 
			      {
			        val = val * 10 + Character.digit(ch, 10);
			      }
			    }
			    return val;
		  }

		  /**
		   * @param key
		   * @return count of spaces in the key
		   */
		  private int extractSpaces(String key) 
		  {
		    int count = 0;
		    for (int i = 0; i < key.length(); ++i) 
		    {
		      if (key.charAt(i) == ' ') 
		      {
		        count++;
		      }
		    }
		    return count;
		  }

		  /**
		   * @param val
		   * @param intBytes
		   */
		  private void intToBytes(int val, byte[] intBytes)
		  {
		    intBytes[0] = (byte) ((val >> 24) & 255);
		    intBytes[1] = (byte) ((val >> 16) & 255);
		    intBytes[2] = (byte) ((val >> 8) & 255);
		    intBytes[3] = (byte) (val & 255);
		  }
		  
	/*
	 * 功能：将long型的数据的低4位字节转换成4个byte，
	 * 等价于无符号的32位int类型数据转换成4个byte，java中没有无符号数据，只能这样取巧的方法
	 * 
	 */
		  private void lownlongToBytes(long val, byte[] lowlongBytes)
		  {
			  lowlongBytes[0] = (byte) ((val >> 24) & 255);
			  lowlongBytes[1] = (byte) ((val >> 16) & 255);
			  lowlongBytes[2] = (byte) ((val >> 8) & 255);
			  lowlongBytes[3] = (byte) (val & 255);
		  }
		  
		  /*
		    * 将字节转换成无符号的整形数字
		    * 例如0xf9，不转换为-7，转换后位249
		    */
		   public int ByteToUnint(byte byteval)
		   {
		   	int intval=0;
		   	for(int i=0; i<=7 ; i++)
		   	{
		   		if( (byteval & (byte)Math.pow(2, i)) != 0)
		   		{
		   			intval = (int) (intval +  Math.pow(2, i));
		   		}
		   	}
		   	return intval;
		   }
		   
		   /*
		    * 功能：提起一行中某一字段后面的值
		    * 例如“host: 192.168.0.1\r\n”，提取“host:”后面值为“192.168.0.1”自动剔去首位空格
		    * String tmp = getfiedl("host: 192.168.0.1\r\n" , "host:");
		    */
		   private static String getfield(String strsour , String field)
		   {
				int beginpos = strsour.indexOf(field);
				int endpos = strsour.indexOf("\r\n", beginpos);
				
				if(-1==beginpos || -1==endpos)
				{
					return null;
				}
				//提取“Sec-WebSocket-Key1:”后面的值
				//String tmp = strsour.substring(beginpos+ field.getBytes().length , endpos); 
				//tmp.trim(); //返回新的字符串，！！！错误的使用方法
				//String tmp2 = tmp.trim() 正确的使用方法
				
				return strsour.substring(beginpos+ field.getBytes().length , endpos).trim();
		   }
}

